package pCloud.client;

import java.util.Random;

import pCloud.config.PCloudConfig;
import pCloud.crypto.kpabe.ACInstanceMessage;
import pCloud.crypto.kpabe.CipherMessage;
import pCloud.crypto.kpabe.KPAbe;
import pCloud.crypto.kpabe.PKMessage;
import pCloud.crypto.kpabe.TKMessage;
import pCloud.crypto.kpabe.UKMessage;
import pCloud.transport.TransportHandle;

/**
 * @author ttadinh Doing crypto stuff for the data owner
 * 
 */
public class DataOwnerCryptoHandle {
	KPAbe crypto;
	TransportHandle cloudConn;
	int id, beta;
	int startingUserIndex;// for generating policies
	boolean isSlidingWindow;
	int[] keyVals;

	public DataOwnerCryptoHandle(TransportHandle cc, int ownerId, int beta,
			boolean isSw) {
		this.cloudConn = cc;
		this.id = ownerId;
		this.beta = beta;
		this.isSlidingWindow = isSw;		
	}

	// init public parameters, and send to the cloud
	public void initialize() throws Exception {
		String[] atts = PCloudConfig.getString("attribute_universe").split(":");
		
		crypto = new KPAbe();
		long start = System.currentTimeMillis();
		byte[] pubK = crypto.initOwner(atts, this.beta);
				
		// then generate TK and UK and send to the cloud		
		this.initializeUserPolicy(pubK);
		long end = System.currentTimeMillis();
		System.out.println("data owner setup: "+(end-start));
		System.out.println("PK length: "+pubK.length);
	}

	/**
	 * send public key, tk to the cloud
	 */
	private void initializeUserPolicy(byte[] pubK) throws Exception {

		String[] vals = PCloudConfig.getStrings("policy");
		String[] ss;
		int userId, alpha;
		byte[] uK, tK, mask;
		UKMessage ukm;
		ACInstanceMessage acim;
			
		int nPolicies = new Integer(PCloudConfig.getString("user_per_stream"))
				.intValue();
		this.startingUserIndex = nPolicies*this.id;
		
		if (!this.isSlidingWindow)
			keyVals = this.generateSimpleKeyValues(nPolicies);
		else
			keyVals = this.generateSimpleAlphaValues(nPolicies);
		
		for (int i = 0; i < nPolicies; i++) {			
			// decryption key for the specific user
			userId = this.startingUserIndex + i;
			uK = crypto.generateUserDecryptionKey(userId);
						
			if (this.isSlidingWindow) {				
				tK = crypto.generateTransformKey(
						this.getPolicyStringSlidingWindow(keyVals[i]), userId);
				System.out.println("TK length: "+tK.length);
				mask = crypto.generateUserMaskValue(keyVals[i]);
				acim = new ACInstanceMessage(id, pubK, userId, tK, true,
						this.beta, keyVals[i]);
				ukm = new UKMessage(userId, pubK, tK, uK, mask, this.beta,
						keyVals[i]);
			} else {

				// generate simple policies, then send
				tK = crypto.generateTransformKey(this.getPolicyStringSimple(keyVals[i]), userId);
				System.out.println("TK length: "+tK.length);
				acim = new ACInstanceMessage(id, pubK, userId, tK, false,
						this.beta, keyVals[i]);
				ukm = new UKMessage(userId, pubK, tK, uK);
			}

			// then send to the cloud
			this.cloudConn.send(ukm);
			this.cloudConn.send(acim);			
		}
	}

	// as it says
	public synchronized void encryptAndSendToTheCloud(int index, int value) throws Exception {		
		CipherMessage cpm;
		String[] encAtts = new String[1];
		encAtts[0] = "key = "+index;
		long start = System.currentTimeMillis();
		long et; 
		if (this.isSlidingWindow) {			
			encAtts[0] = "key = "+index;
			byte[] cph = crypto.enc_slidingWindow(encAtts, value, index);
			byte[] ep = crypto.extractOriginalCipher(cph);
			cpm = new CipherMessage(id, cph, ep, true, index);
		} else {			
			byte[] cph = crypto.enc_simple(encAtts, value);
			et = System.currentTimeMillis();			
			byte[] ep = crypto.extractOriginalCipher(cph);
			et = System.currentTimeMillis();			
			cpm = new CipherMessage(id, cph, ep, false, index);			
		}
		long end = System.currentTimeMillis();
		System.out.println("encryption time = "+(end-start));
		this.cloudConn.send(cpm);
	}

	private String getPolicyStringSimple(int keyVal) {
		String[] atts = PCloudConfig.getString("attribute_universe").split(":");
		String s = "key = " + keyVal;
		if (atts.length == 1)
			return s;
		for (int i = 1; i < atts.length; i++) {
			s += " or " + atts[i];
		}
		return s;
	}

	private String getPolicyStringSlidingWindow(int alpha) {
		String[] atts = PCloudConfig.getString("attribute_universe").split(":");
		String s = "key >= " + alpha;
		if (atts.length == 1)
			return s;
		for (int i = 1; i < atts.length; i++) {
			s += " or " + atts[i];
		}
		return s;
	}

	// used as keys to construct simple XACML policy
	private int[] generateSimpleKeyValues(int n) {
		int[] ret = new int[n];

		if (PCloudConfig.getBoolean("fixed_key"))
			for (int i = 0; i < n; i++)
				ret[i] = new Integer(PCloudConfig.getString("fixed_key_val"))
						.intValue();
		else {
			Random rand = new Random(PCloudConfig.getRandomSeed() + this.id);
			int max = new Integer(PCloudConfig.getString("max_key")).intValue();
			for (int i = 0; i < n; i++)
				ret[i] = rand.nextInt(max);
		}
		return ret;
	}

	// used as keys to construct sliding window XACML policy
	private int[] generateSimpleAlphaValues(int n) {
		int[] ret = new int[n];

		Random rand = new Random(PCloudConfig.getRandomSeed() + this.id);
		int max = new Integer(PCloudConfig.getString("max_key")).intValue();
		for (int i = 0; i < n; i++)
			ret[i] = rand.nextInt(max*2);

		return ret;
	}
}
